using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
//using polytest.Poly;
using Microsoft.Xna.Framework.Content;
using GameStateManagementSample.Classes;

namespace PolyDecomp
{
    public class EarClipping
    {
        //static int maxVertices = 2 << 8;
        //float[] x = new float[maxVertices];
        //float[] y = new float[maxVertices];
        List<Vector2> vertices = new List<Vector2>();
        //int numVertices = 0;
        Texture2D point;
        Texture2D line5;
        Texture2D line3;
        Texture2D line10;
        SpriteFont font;

        //bool pmousePressed = false;
        MouseState prevMouseState;
        MouseState mouse;
        KeyboardState keyboard;
        KeyboardState prevKeyboardState;
        public EarClipping(GraphicsDevice gd, ContentManager content)
        {
            LineBatchTester.LineBatch.Init(gd);
            point = content.Load<Texture2D>("point");
            line5 = content.Load<Texture2D>("line5");
            line3 = content.Load<Texture2D>("line3");
            line10 = content.Load<Texture2D>("line10");
            font = content.Load<SpriteFont>("gamefont");
        }

        public void processMouse()
        {
            mouse = Mouse.GetState();
            keyboard = Keyboard.GetState();

            if (keyboard.IsKeyDown(Keys.Space) && !prevKeyboardState.IsKeyDown(Keys.Space)) { show = !show; }
            prevKeyboardState = keyboard;
            if (mouse.LeftButton == ButtonState.Pressed && prevMouseState.LeftButton != mouse.LeftButton)
            {

                vertices.Add(new Vector2(mouse.X, mouse.Y));

            }
            if (mouse.RightButton == ButtonState.Pressed)
            {
                vertices.Clear();
            }
            prevMouseState = mouse;

        }

        public void draw(SpriteBatch sb)
        {
            //background(0);
            //stroke(255);


            //if (mousePressed && !pmousePressed && numVertices < maxVertices){
            //  x[numVertices] = mouseX; y[numVertices] = mouseY;
            //  numVertices++;
            //}

            //if (keyPressed && key == 'r'){
            //  x = new float[maxVertices];
            //  y = new float[maxVertices];
            //  numVertices = 0;
            //}
            //float[] xtmp = new float[maxVertices];
            //float[] ytmp = new float[maxVertices];
            List<Vector2> vertTmp = new List<Vector2>(vertices);
            if (GeometryHelper.IsClockwisePolygon(vertTmp))
            {
                //for (int i = 0; i < numVertices; i++)
                //{
                //    xtmp[i] = x[numVertices - 1 - i];
                //    ytmp[i] = y[numVertices - 1 - i];
                //}
                vertTmp.Reverse();
            }


            Triangle[] triangulated = Triangulation.triangulatePolygon(vertTmp);
            //Triangle[] triangulated = Triangulation.triangulatePolygon(x, y, numVertices);
            Polygon[] polys = Triangulation.polygonizeTriangles(triangulated);
            if (triangulated != null)
            {
                for (int i = 0; i < polys.Length; i++)
                {
                    if (polys[i] != null) polys[i].draw(sb);
                    var pnt = GeometryHelper.Compute2DPolygonCentroid(polys[i].vertices);
                    sb.Draw(point, pnt - new Vector2(point.Width / 2, point.Height / 2), Color.White);
                    if (show)
                        drawSpiderWeb(sb, polys[i].vertices);
                }
            }


            if (vertices.Count > 0)
            {
                Vector2 last = vertices.Last();
                foreach (Vector2 vert in vertices)
                {

                    LineBatchTester.LineBatch.DrawLine(sb, Color.Red, vert, last);
                    sb.Draw(point, vert - new Vector2(point.Width / 2, point.Height / 2), Color.White);
                    last = vert;
                }
                var cent = GeometryHelper.Compute2DPolygonCentroid(vertices);
                sb.Draw(point, cent - new Vector2(point.Width / 2, point.Height / 2), Color.White);
            }


            //pmousePressed = mousePressed;
        }
        bool show = true;


        void drawSpiderWeb(SpriteBatch sb, List<Vector2> pol)
        {
            var poly = new List<Vector2>(pol);
            if (!GeometryHelper.IsClockwisePolygon(poly))
            {
                //for (int i = 0; i < numVertices; i++)
                //{
                //    xtmp[i] = x[numVertices - 1 - i];
                //    ytmp[i] = y[numVertices - 1 - i];
                //}
                poly.Reverse();
            }
            var center = GeometryHelper.Compute2DPolygonCentroid(poly);

            float sum = 0;
            float min = 2000;
            float max = 0;
            foreach (Vector2 vert in poly)
            {
                var len = Vector2.Distance(center, vert);
                if (len > max) max = len;
                if (len < min) min = len;
                sum += len;
            }
            float avg = sum / poly.Count;

            int segments = (int)(avg / 10);

            var last = poly.Last();

            var lastAngle = VectorToAngle(last - center);
            lastAngle %= MathHelper.Pi * 2;
            if (lastAngle < 0) lastAngle += MathHelper.Pi * 2;

            var previousPoint = last;

            var currentAngle = lastAngle + MathHelper.Pi / 6;

            float remain = 0;
            foreach (Vector2 vert in poly)
            {
                //var endX = centerX + radius * cos(angle);
                //var endY = centerY + radius * sin(angle);
                float angle = VectorToAngle(vert - center) + remain;

                //var ang = angle - currentAngle;
                angle %= MathHelper.Pi * 2;
                if (angle < 0) angle += MathHelper.Pi * 2;

                lastAngle %= MathHelper.Pi * 2;
                if (lastAngle < 0) lastAngle += MathHelper.Pi * 2;


                if (lastAngle > angle)
                {
                    lastAngle -= MathHelper.Pi * 2;

                }
                currentAngle %= MathHelper.Pi * 2;
                if (lastAngle < 0 && angle > 0 && lastAngle <= currentAngle - MathHelper.Pi * 2 && currentAngle - MathHelper.Pi * 2 <= angle)
                {
                    currentAngle -= MathHelper.Pi * 2;
                }

                //if (currentAngle < 0) currentAngle += MathHelper.Pi * 2;
                //currentAngle %= MathHelper.Pi * 2;

                while (lastAngle <= currentAngle && currentAngle <= angle)
                {
                    var vect = AngleToVector(currentAngle) * 200 + center;
                    //vect *= 50;

                    var col = Color.Yellow;

                    var v = GeometryHelper.GetTrajectoryIntersectionOnLineDouble(last, vert, center, vect);
                    if (v != null)
                    {
                        vect = v.Value;
                        col = Color.Green;
                    }
                    else
                    {
                        col = Color.Red;
                    }


                    //if ((GeometryHelper.RadianToDegree(currentAngle - lastAngle) + 360) % 360 < 15)
                    //    drawSpiderWebSegment(sb, center, vect, last, avg, segments);
                    //else
                    var p1 = vect;
                    var p2 = previousPoint;

                    if ((GeometryHelper.RadianToDegree(angle - currentAngle) + 360) % 360 < 15)
                    {
                        p1 = vert;
                        col = Color.LightBlue;
                    }
                    if ((GeometryHelper.RadianToDegree(currentAngle - lastAngle) + 360) % 360 < 15)
                    {
                        p1 = last;
                        col = Color.LightGreen;
                    }
                    drawSpiderWebSegment(sb, center, p1, p2, avg, min, segments, col);

                    sb.DrawString(font, Math.Round(GeometryHelper.RadianToDegree(currentAngle), 0).ToString(), vect, Color.White);
                    currentAngle += MathHelper.Pi / 6;
                    previousPoint = p1;
                }


                //if (ang <= MathHelper.Pi / 8)
                //{
                //    remain = ang;
                //    continue;
                //}
                //else if (MathHelper.Pi / 8 < ang && ang <= MathHelper.Pi / 4)
                //{
                //    drawSpiderWebSegment(sb, center, vert, last, avg, segments);
                //    currentAngle = angle;

                //    remain = MathHelper.Pi / 6 - ang;
                //}
                //else
                //{
                //    while (angle - currentAngle > MathHelper.Pi / 8)
                //    {
                //        var vect = AngleToVector(currentAngle + MathHelper.Pi / 6) + center;
                //        vect *= 10;
                //        //vect = GeometryHelper.GetTrajectoryIntersectionOnLineDouble(last, vert, center, vect).Value;//todo check
                //        drawSpiderWebSegment(sb, center, vect, last, avg, segments);

                //        currentAngle += MathHelper.Pi / 6;
                //    }

                //}


                last = vert;
                lastAngle = VectorToAngle(last - center);
            }
            if (previousPoint != poly.Last())
                drawSpiderWebSegment(sb, center, poly.Last(), previousPoint, avg, min, segments, Color.White);
        }
        void drawSpiderWeb_debug(SpriteBatch sb, List<Vector2> pol)
        {
            var poly = new List<Vector2>(pol);
            if (!GeometryHelper.IsClockwisePolygon(poly))
            {
                //for (int i = 0; i < numVertices; i++)
                //{
                //    xtmp[i] = x[numVertices - 1 - i];
                //    ytmp[i] = y[numVertices - 1 - i];
                //}
                poly.Reverse();
            }
            var center = GeometryHelper.Compute2DPolygonCentroid(poly);

            float sum = 0;
            float min = 2000;
            float max = 0;
            foreach (Vector2 vert in poly)
            {
                var len = Vector2.Distance(center, vert);
                if (len > max) max = len;
                if (len < min) min = len;
                sum += len;
            }
            float avg = sum / poly.Count;

            int segments = (int)(avg / 10);

            var last = poly.Last();

            var lastAngle = VectorToAngle(last - center);
            lastAngle %= MathHelper.Pi * 2;
            if (lastAngle < 0) lastAngle += MathHelper.Pi * 2;

            var previousPoint = last;

            var currentAngle = lastAngle + MathHelper.Pi / 6;

            float remain = 0;
            foreach (Vector2 vert in poly)
            {
                //var endX = centerX + radius * cos(angle);
                //var endY = centerY + radius * sin(angle);
                float angle = VectorToAngle(vert - center) + remain;

                //var ang = angle - currentAngle;
                angle %= MathHelper.Pi * 2;
                if (angle < 0) angle += MathHelper.Pi * 2;

                lastAngle %= MathHelper.Pi * 2;
                if (lastAngle < 0) lastAngle += MathHelper.Pi * 2;


                if (lastAngle > angle)
                {
                    lastAngle -= MathHelper.Pi * 2;

                }
                currentAngle %= MathHelper.Pi * 2;
                if (lastAngle < 0 && angle > 0 && lastAngle <= currentAngle - MathHelper.Pi * 2 && currentAngle - MathHelper.Pi * 2 <= angle)
                {
                    currentAngle -= MathHelper.Pi * 2;
                }

                //if (currentAngle < 0) currentAngle += MathHelper.Pi * 2;
                //currentAngle %= MathHelper.Pi * 2;

                while (lastAngle <= currentAngle && currentAngle <= angle)
                {
                    var vect = AngleToVector(currentAngle) * 200 + center;
                    //vect *= 50;

                    var col = Color.Yellow;

                    var v = GeometryHelper.GetTrajectoryIntersectionOnLineDouble(last, vert, center, vect);
                    if (v != null)
                    {
                        vect = v.Value;
                        col = Color.Green;
                    }
                    else
                    {
                        col = Color.Red;
                    }


                    //if ((GeometryHelper.RadianToDegree(currentAngle - lastAngle) + 360) % 360 < 15)
                    //    drawSpiderWebSegment(sb, center, vect, last, avg, segments);
                    //else
                    var p1 = vect;
                    var p2 = previousPoint;

                    if ((GeometryHelper.RadianToDegree(angle - currentAngle) + 360) % 360 < 15)
                    {
                        p1 = vert;
                        col = Color.LightBlue;
                    }
                    if ((GeometryHelper.RadianToDegree(currentAngle - lastAngle) + 360) % 360 < 15)
                    {
                        p1 = last;
                        col = Color.LightGreen;
                    }
                    drawSpiderWebSegment(sb, center, p1, p2, avg, min, segments, col);

                    sb.DrawString(font, Math.Round(GeometryHelper.RadianToDegree(currentAngle), 0).ToString(), vect, Color.White);
                    currentAngle += MathHelper.Pi / 6;
                    previousPoint = p1;
                }


                //if (ang <= MathHelper.Pi / 8)
                //{
                //    remain = ang;
                //    continue;
                //}
                //else if (MathHelper.Pi / 8 < ang && ang <= MathHelper.Pi / 4)
                //{
                //    drawSpiderWebSegment(sb, center, vert, last, avg, segments);
                //    currentAngle = angle;

                //    remain = MathHelper.Pi / 6 - ang;
                //}
                //else
                //{
                //    while (angle - currentAngle > MathHelper.Pi / 8)
                //    {
                //        var vect = AngleToVector(currentAngle + MathHelper.Pi / 6) + center;
                //        vect *= 10;
                //        //vect = GeometryHelper.GetTrajectoryIntersectionOnLineDouble(last, vert, center, vect).Value;//todo check
                //        drawSpiderWebSegment(sb, center, vect, last, avg, segments);

                //        currentAngle += MathHelper.Pi / 6;
                //    }

                //}


                last = vert;
                lastAngle = VectorToAngle(last - center);
            }
            if (previousPoint != poly.Last())
                drawSpiderWebSegment(sb, center, poly.Last(), previousPoint, avg, min, segments, Color.White);
        }

        private void drawSpiderWebSegment_old(SpriteBatch sb, Vector2 center, Vector2 vert, Vector2 last, float avg, int segments, Color col)
        {
            LineBatchTester.LineBatch.DrawLine(sb, col, vert, center);
            for (int i = 1; i < segments; i++)
            {
                float t = (float)Math.Pow(0.7, i);

                float koef = 1;
                if (avg < Vector2.Distance(center, vert))
                {
                    koef = (float)Math.Pow(avg / Vector2.Distance(center, vert), 2);
                    // if (koef == 0) koef = 1;
                }
                if (t * koef > 1) continue;
                var p1 = GeometryHelper.getPointAtDistance(t * koef, center, vert);

                koef = 1;
                if (avg < Vector2.Distance(center, last))
                {
                    koef = (float)Math.Pow(avg / Vector2.Distance(center, last), 2);
                    // if (koef == 0) koef = 1;
                }

                if (t * koef > 1) continue;
                var p2 = GeometryHelper.getPointAtDistance(t * koef, center, last);

                LineBatchTester.LineBatch.DrawLine(sb, Color.Yellow, p1, p2);
            }
        }
        private void drawSpiderWebSegment(SpriteBatch sb, Vector2 center, Vector2 vert, Vector2 last, float avg, float min, int segments, Color col)
        {
            DrawLine(sb, vert, center);
            for (int i = 1; i < segments; i++)
            {
                float t = (float)Math.Pow(0.8, i);

                float koef = 1;
                //if (avg < Vector2.Distance(center, vert))
                //{
                koef = (float)Math.Pow(avg / Vector2.Distance(center, vert), 0.75);
                // if (koef == 0) koef = 1;
                //}
                Vector2 p1;
                if (t * koef >= 1)
                {
                    p1 = vert;
                }
                else
                {
                    p1 = GeometryHelper.getPointAtDistance(t * koef, center, vert);
                }

                koef = 1;
                //if (avg < Vector2.Distance(center, last))
                //{
                koef = (float)Math.Pow(avg / Vector2.Distance(center, last), 0.75);
                // if (koef == 0) koef = 1;
                //}
                Vector2 p2;
                if (t * koef >= 1)
                {
                    p2 = last;
                }
                else
                {
                    p2 = GeometryHelper.getPointAtDistance(t * koef, center, last);
                }

                if (!(p1 == vert && p2 == last))
                    DrawLine(sb, p1, p2);
            }
        }
        private void drawSpiderWebSegment_debug(SpriteBatch sb, Vector2 center, Vector2 vert, Vector2 last, float avg, float min, int segments, Color col)
        {
            for (int i = 1; i < segments; i++)
            {
                float t = (float)Math.Pow(0.8, i);

                float koef = 1;
                //if (avg < Vector2.Distance(center, vert))
                //{
                koef = (float)Math.Pow(avg / Vector2.Distance(center, vert), 0.75);
                // if (koef == 0) koef = 1;
                //}
                Vector2 p1;
                if (t * koef >= 1)
                {
                    p1 = vert;
                }
                else
                {
                    p1 = GeometryHelper.getPointAtDistance(t * koef, center, vert);
                }

                koef = 1;
                //if (avg < Vector2.Distance(center, last))
                //{
                koef = (float)Math.Pow(avg / Vector2.Distance(center, last), 0.75);
                // if (koef == 0) koef = 1;
                //}
                Vector2 p2;
                if (t * koef >= 1)
                {
                    p2 = last;
                }
                else
                {
                    p2 = GeometryHelper.getPointAtDistance(t * koef, center, last);
                }

                if (!(p1 == vert && p2 == last))
                    LineBatchTester.LineBatch.DrawLine(sb, Color.Yellow, p1, p2);
            }

            LineBatchTester.LineBatch.DrawLine(sb, col, vert, center);
        }
        public static Vector2 AngleToVector(float angle)
        {
            return new Vector2((float)Math.Sin(angle), -(float)Math.Cos(angle));
        }

        public static float VectorToAngle(Vector2 vector)
        {
            return (float)Math.Atan2(vector.X, -vector.Y);
        }

        public void DrawLine( SpriteBatch spriteBatch, Vector2 start, Vector2 end)
        {
            spriteBatch.Draw(line3, start, null, Color.White,
                             (float)Math.Atan2(end.Y - start.Y, end.X - start.X),
                             new Vector2(0f, (float)line3.Height / 2),
                             new Vector2(Vector2.Distance(start, end), 1f),
                             SpriteEffects.None, 0f);
        }
    }
}